package launcher;

import java.io.File;
import java.io.IOException;
import java.util.Vector;

import classifier.KMeans;
import classifier.LabeledData;
import classifier.Pair;

import illustration.Curve;
import illustration.ClusteringGraph;

import org.jfree.chart.ChartUtilities;
import org.jfree.ui.RefineryUtilities;

import data.Corpus;
import data.Document;

public class BigStatsMain {
	public static void main(String[] args) {
		int runs = 20;
		Corpus corp = new Corpus(CORPUS_PATH);

		Vector<Curve> entries = new Vector<Curve>();
		Curve purity_curve = new Curve("Purity");
		Curve randindex_curve = new Curve("RandIndex");

		for (int K = 2; K <= 10; K += 2) {
			System.out.println("K = " + K);
			Vector<Double> purityVector = new Vector<Double>();
			Vector<Double> riVector = new Vector<Double>();
			
			for (int j = 0; j < 10; j++) {
				KMeans<Document> kmeans = new KMeans<Document>(toDocuments(corp
						.getTestData(0), corp), K);
				kmeans.initialize();
				
				double purity=0.0;
				double randindex=0.0;

				for (int i = 0; i < runs; i++) {
					kmeans.run();
					
					purity = puritySeverin(kmeans.getClustering(), corp
							.getTestDataWithLabels(0), K); 

					randindex = RandIndex(kmeans.getClustering(), corp
							.getTestDataWithLabels(0));
				}
				purityVector.add(purity);
				riVector.add(randindex);
			}
			
			Double sumPurity =0.0;
			Double sumri = 0.0;
			for (int i = 0; i < purityVector.size(); i++) {
				sumPurity+= purityVector.get(i);
				sumri+=riVector.get(i);
			}
			purity_curve.addValue(sumPurity/purityVector.size());
			randindex_curve.addValue(sumri/riVector.size());

		}

		entries.add(purity_curve);
		entries.add(randindex_curve);
		
		ClusteringGraph CGraph = new ClusteringGraph(entries);
		CGraph.pack();
		RefineryUtilities.centerFrameOnScreen(CGraph);
		CGraph.setVisible(true);
	}

	private static double puritySeverin(
			Vector<Pair<Document, Integer>> clusters,
			Vector<LabeledData<Vector<String>>> trueLabels, int K) {
		int[] map = new int[K];

		// Create a mapping cluster to classes
		for (int i = 0; i < K; i++) {
			int[] counts = new int[K];
			// Count the number of data points that are assigned to each class
			// in the cluster
			for (int j = 0; j < clusters.size(); j++) {
				if (clusters.get(j).getSecond() == i) {
					counts[trueLabels.get(j).getLabel()]++;
				}
			}
			// System.out.println("counts" + counts[0]);
			// Find the class in the cluster that is most frequent
			map[i] = getIndexOfMax(counts);
			// System.out.println("map" + i + map[i]);
		}

		// Now we can compare our solution with the real labels
		double count = 0;
		for (int i = 0; i < clusters.size(); i++) {
			if (map[clusters.get(i).getSecond()] == trueLabels.get(i)
					.getLabel()) {
				count++;
			}
		}

		return count / clusters.size();
	}

	private static double RandIndex(Vector<Pair<Document, Integer>> clusters,
			Vector<LabeledData<Vector<String>>> trueLabels) {
		double randindex;

		// C_p: classlabel of sample p
		// K_p: clusterlabel of sample p

		double a = 0; // number of pairs with C_p == C_p' and K_p == K_p'
		double b = 0; // number of pairs with C_p == C_p' and K_p != K_p'
		double c = 0; // number of pairs with C_p != C_p' and K_p == K_p'
		double d = 0; // number of pairs with C_p != C_p' and K_p != K_p'

		for (int i = 1; i < clusters.size(); i++) {
			for (int j = 0; j < i; j++) {
				if (clusters.get(i).getSecond() == clusters.get(j).getSecond()) {
					if (trueLabels.get(i).getLabel() == trueLabels.get(j)
							.getLabel()) {
						a++;
					} else {
						c++;
					}
				} else {
					if (trueLabels.get(i).getLabel() == trueLabels.get(j)
							.getLabel()) {
						b++;
					} else {
						d++;
					}
				}
			}
		}

		randindex = (a + d) / (a + b + c + d);

		return randindex;
	}

	private static int getIndexOfMax(int[] a) {
		int max = Integer.MIN_VALUE;
		int maxIdx = Integer.MIN_VALUE;

		for (int j = 0; j < a.length; j++) {
			if (a[j] > max) {
				max = a[j];
				maxIdx = j;
			}
		}
		return maxIdx;
	}

	private static Vector<Document> toDocuments(Vector<Vector<String>> data,
			Corpus corp) {
		Vector<Document> result = new Vector<Document>();
		for (Vector<String> str : data) {
			result.add(new Document(str, corp));
		}
		return result;
	}

	private static final String CORPUS_PATH = "data" + File.separator
			+ "clustering_corpus";

}
